package Segments;

import Entities.Car;
import event.*;
import Util.SortedHashtable;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;

/**
 * Esta clase representa el segmento, una generalizacion para la calle y la interseccion
 * <B>por ejemplo</B>
 * <I>les voy a mostrar como utilizarla</I>
 * <PRE>es por eso que estos son los prerequisitos</PRE>
 */
public class Segment implements java.io.Serializable {

    private int segmentId;
    private double size;
    Event rootControl;
    Event SQS;
    private boolean isPozo;
    private SortedHashtable cars;
    private SortedHashtable parkedObjects;
    int SE = 1; //Stop Event
    int CE = 2;//Cross Event
    int LCE = 3; //Light Change Event
    int CLE = 5; //Change Lane Event
    int CLEE = 6; //Change Lane End Event
    int ALE = 7; //Arrival Link Event
    int LLE = 8; //Leave Link Event
    int BE = 9; //Begin Event
    int AE = 10; //Arrival Event
    int WSE = 11; //Warning Event, as a stop event, but dont stop
    int WBE = 12; // warning Event, as a begin event, but actually for a dont stop event
    double POS_FULL;
    boolean LOG;
    private int[] outSegments;
    private int index;
    
    private int x1;
    private int y1;
    private int x2;
    private int y2;
    private int x3;
    private int y3;
    private int x4;
    private int y4;

    public Segment(Event rootControl) {


        this.rootControl = rootControl;


        SQS = new Event();
        SQS.setTime(0.0);
        SQS.anterior = SQS;
        SQS.derecho = SQS;
        SQS.izquierdo = SQS;
        SQS.siguiente = SQS;
        SQS.putName("SQS");
        cars = new SortedHashtable();
        parkedObjects = new SortedHashtable();
        POS_FULL = 0.0;
        LOG = true;

        outSegments = new int[20];
        index = 0;

    }

    public Segment() {


        parkedObjects = new SortedHashtable();
        outSegments = new int[20];
        cars = new SortedHashtable();
        
        LOG = true;


        index = 0;

    }

    public Segment(int id, Event rootControl, boolean isPozo) {

        this.segmentId = id;
        this.rootControl = rootControl;
        this.isPozo = isPozo;
        SQS = new Event();
        SQS.setTime(0.0);
        SQS.anterior = SQS;
        SQS.derecho = SQS;
        SQS.izquierdo = SQS;
        SQS.siguiente = SQS;
        SQS.putName("SQS");
        cars = new SortedHashtable();
        parkedObjects = new SortedHashtable();
        outSegments = new int[20];
        POS_FULL = 0.0;
        LOG = true;


        index = 0;

    }
     public void addConnection(int out) {
        this.outSegments[index] = out;
        index++;
    }
    public void addParkedObject(ParkedObject parkedObject) {
        parkedObjects.put(parkedObject, 0.0);
    }

    public void agregarEvento(double evTime, int typeEvent, Object objectId, double eventPosition, int desiredStatus, int lane) {
        Event x = SQS;
        Event y, z = null;
        while (evTime >= x.siguiente.evTime() && x.siguiente != SQS) {
            x = x.siguiente;
        }
        y = x.siguiente;

        if (typeEvent == AE) {

            x.siguiente = new CarArrival(this);

        }

        if (typeEvent == CE) {
            x.siguiente = new CarLeaveLink(this);

        }

        if (typeEvent == ALE) {
            x.siguiente = new CarArrivalLink(this);

        }

        if (typeEvent == SE || typeEvent == WSE) {
            x.siguiente = new CarStop(this);


        }
        if (typeEvent == BE || typeEvent == WBE) {
            x.siguiente = new CarStart(this);


        //new CarStart().schedule(startTime,WBE,objectId(),arrivalPosition,segmentoPrioritario,segmentoPrioritario);
        }
        if (typeEvent == LCE) {
            x.siguiente = new LightChangeEvent(this);


        }
        x.siguiente.schedule(evTime, typeEvent, objectId, eventPosition, desiredStatus, lane);
        x.siguiente.setTime(evTime);

        if (x == SQS && y == SQS) {

            z = rootControl;
            while (x.siguiente.evTime() >= z.derecho.evTime() && z.derecho != rootControl) {
                z = z.derecho;
            }

            x.siguiente.derecho = z.derecho;
            x.siguiente.izquierdo = z;
            z.derecho.izquierdo = x.siguiente;
            z.derecho = x.siguiente;
            x.siguiente.eventoPotencial = true;
        }
        if (y.eventoPotencial == true) {
            // is a potential event,
            // so de new event must interachange with the 
            //control too.
            z = y;

            while (x.siguiente.evTime() < z.izquierdo.evTime() && z.izquierdo != rootControl) {
                z = z.izquierdo;
            }



        }


        y.anterior = x.siguiente;
        x.siguiente.anterior = x;
        y.anterior.siguiente = y;


        if (y.eventoPotencial == true) {

            y.eventoPotencial = false;

            x.siguiente.eventoPotencial = true;
            //hay que poner el evento despues de z

            x = x.siguiente;
            x.izquierdo = z.izquierdo;
            z.izquierdo = x;
            x.izquierdo.derecho = x;
            x.derecho = z;

            //ahora hay que eliminar y en caso de que se haya sustiuido
            //por un evento menor, es decir en caso de que y.potential = true

            y.izquierdo.derecho = y.derecho;
            y.derecho.izquierdo = y.izquierdo;
            y.derecho = null;
            y.izquierdo = null;



        }




    }
    public Car findCar(int carId){
        Car tmp;
        for(int i=0; i<cars.size();i++){
            tmp = (Car)cars.getKeyAt(i);
            if(tmp.getId() == carId){
                return tmp;
            }
        }
        return null;
        
    }
    
    public String getXMLData(){
        
        String xmlString = "<street ";
        /*
         * 
         * <street id="0" nolanes="1" x1="0" y1="132" x2="132" y2="132" x3="0"
         y3="140" x4="132" y4="140" length="132.00" toId1="4"  isPozo="false" >streetName               
        </street>

         */
        xmlString= xmlString + "id=\""+getId()+"\" "+
                "nolanes=\"1\" "+
                "x1=\""+x1+"\" "+
                "y1=\""+y1+"\" "+
                "x2=\""+x2+"\" "+
                "y2=\""+y2+"\" "+
                "x3=\""+x3+"\" "+
                "y3=\""+y3+"\" "+
                "x4=\""+x4+"\" "+
                "y4=\""+y4+"\" "+
                "length=\""+this.size+"\" ";
        
        for(int i=0;i<index;i++){
            xmlString=xmlString+"toId"+(i+1)+"=\""+outSegments[i]+"\" ";
        }
        xmlString = xmlString+"isPozo =\""+isPozo+"\" "+">streetName </street>";
        
               
        return xmlString;
    }
    public void paintSegment(Graphics g) {
       if(g!=null){
        g.setColor(Color.RED);
        g.drawLine(x1, y1, x2, y2);
        g.drawLine(x3, y3, x4, y4);
        g.drawLine(x1, y1, x3, y3);
        g.drawLine(x2, y2, x4, y4);
        
        if(x1 != x2)
        g.drawString(this.segmentId+"", x3+5, y3);
        else
            g.drawString(this.segmentId+"", x3, y3+10);
       }else
           System.out.println("Error g es null");
    }

    public void paintObjects(Graphics g) {
        int y = 0;
        int x = 0;
        for(int i=0; i<cars.size();i++){
            
            if (x1 == x3) {
                y = y1+4;//8 es el ancho de las calles en esta prueba y por lo tanto cuatro es la mitad
                x = x1 + cars.getValueOf(cars.getKeyAt(i)).intValue();
            } else if (y1 == y3) {
                y = y1 + cars.getValueOf(cars.getKeyAt(i)).intValue();
                x = x1 + 4;
            }
            
            g.setColor(Color.BLUE);
            int h = 9;
            int w = 6;
            
            g.fillRect(x - w / 2, y - h / 2, w, h);
            g.setColor(Color.black);
            g.drawRect(x - w / 2, y - h / 2, w - 1, h - 1);
        //g.drawString(n.lbl, x-w/2+2, y+h/2-2);
        }
       }
    
    public int getCarsNumber(){
        return cars.size();
    }
    
    public Event getRoot() {
        return SQS;
    }
   
    public void PutStreetId(int street_Id) {
        this.segmentId = street_Id;

    }

    public int getId() {
        return segmentId;
    }

    public double getSize() {
        return size;
    }

    public void setSize(double size) {
        this.size = size;
    }

    public void putCar(Car key, double value) {
        //System.out.println(key.getName());
        cars.put(key, new Double(value));
    }

    public boolean isPozo() {
        return this.isPozo;
    }

    public void removeCar(Car key) {
        cars.remove(key);
    }

    public void setAttribute(String attributeName, String attributeValue) {


        if (attributeName.equals("id")) {
            this.segmentId = Integer.parseInt(attributeValue);
        } else if (attributeName.equals("length")) {
            this.size = Double.parseDouble(attributeValue);
        } else if (attributeName.equals("width")) {
            this.size = Double.parseDouble(attributeValue);
        } else if (attributeName.equals("toId1") || attributeName.equals("toId2")) {
            this.addConnection(Integer.parseInt(attributeValue));
        } else if (attributeName.equals("isPozo")) {
            if (attributeValue.equals("true")) {
                this.isPozo = true;
            } else {
                this.isPozo = false;
            }
        } else {
            if (attributeName.equals("x1")) {
                this.x1 = Integer.parseInt(attributeValue);
            } else {
                if (attributeName.equals("x2")) {
                    this.x2 = Integer.parseInt(attributeValue);
                } else {
                    if (attributeName.equals("x3")) {
                        this.x3 = Integer.parseInt(attributeValue);
                    } else if (attributeName.equals("x4")) {
                        this.x4 = Integer.parseInt(attributeValue);
                    } else if (attributeName.equals("y1")) {
                        this.y1 = Integer.parseInt(attributeValue);
                    } else if (attributeName.equals("y2")) {
                        this.y2 = Integer.parseInt(attributeValue);
                    } else if (attributeName.equals("y3")) {
                        this.y3 = Integer.parseInt(attributeValue);
                    } else if (attributeName.equals("y4")) {
                        this.y4 = Integer.parseInt(attributeValue);
                    }
                }

            }
        }



    // throw new UnsupportedOperationException("segment setAttributes not implemented");
    }

    public boolean isFull(double atTime) {
        /*is full al instante atTime?
         */
        boolean isFull = true;
        /*
         *este seria el algoritmo para verificar si esta lleno o no
         */

        if (!this.isEmpty()) {
            if (LOG) {
                System.out.println("No esta vacio");
            }
            Car carTemporal = (Car) this.cars.getKeyAt(0);
            // obtengo el ultimo que llego, LIFO
            if (carTemporal.getPosition() == 0.0) {
                if (carTemporal.getTypeSeq() == CE) {
                    if (atTime >= carTemporal.getBegEvTime()) {
                        //Si inicio su traves�a hacia un cambio de segmento, cuando se desea agregar un nuevo evento,
                        //entonces no perjudica y se regresa un false.
                        isFull = false;
                    } else {
                        isFull = true;
                    }
                } else {
                    if (carTemporal.getTypeSeq() == SE || carTemporal.getTypeSeq() == WSE) {
                        if (atTime <= carTemporal.getEndEvTime()) {
                            if (carTemporal.getStopDeltaP() != 0) {
                                //esta en proceso de avanzar y parara mas adelante
                                isFull = false;
                            } else {
                                isFull = true;
                            }
                        } else {
                            isFull = true;
                        //PONER CUIDADO AQUI, PORQUE QUE TAL SI AL ARRANCAR DECIDE VOVLERSE A QUEDAR PARADO UN wse POR EJEMPLO,
                        //MEJOR HAQY QUE PONER TRUE.
                        }
                    }
                }
            } else {
                isFull = false;
                if (LOG) {
                    System.out.println("No esta vacio, pero nadie esta en la posicion 0");
                }
            }
        } else {
            isFull = false;
            if (LOG) {
                System.out.println("esta vacio");
            }
        }
        return isFull;

    }

    private boolean isEmpty() {
        if (LOG) {
            System.out.println("Cars esta >" + this.cars.isEmpty());
        }
        return this.cars.isEmpty();
    }

    private Integer givePosLastCar() {
        return cars.givePositionLastCar();
    }

   

    public int getConnections() {
        Random generator = new Random();
        int randomIndex = generator.nextInt(50);
        if (index > 1) {
            if (randomIndex <= 25) {
                return outSegments[0];
            } else {
                return outSegments[1];
            }
        } else {
            if(index == 1)
            return outSegments[0];
            else
                return -1;
        }
    }
} // end Segment




